package org.stephen.recommend.algorithms;

import org.stephen.recommend.enums.LanguageType;
import org.stephen.recommend.common.RedisKeyPrefix;
import org.stephen.recommend.config.PropGetKit;
import org.stephen.recommend.model.RecommendedArticles;
import org.stephen.recommend.model.SimilarityUsers;
import org.stephen.recommend.model.UserTags;
import org.stephen.recommend.model.ViewedArticles;
import org.stephen.recommend.util.CommonUtils;
import org.stephen.recommend.util.RedisUtils;
import org.stephen.recommend.util.SerializeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * ClassName: AbstractRecommender
 * Description: 推荐父类，推荐算法选择实现父类的方法
 * Date: 2020/12/8 20:36
 *
 * @author stephen.qiu
 * @version 1.0
 */
@DependsOn("redisTemplate")
public abstract class AbstractRecommender {
    @Autowired
    protected PropGetKit propGetKit;
    @Autowired
    protected RedisUtils redisUtils;
//    @Autowired
//    AsyncService asyncService;

    /**
     * 针对所有用户返回推荐结果
     *
     * @param languageType 语言环境
     *                     未实现
     */
    protected void recommend(LanguageType languageType) {
        //TODO
    }

    /**
     * 针对用户集合返回推荐结果
     *
     * @param blockArticleIds 用户屏蔽的文章
     * @param userId          用户id集合
     * @param languageType    语言环境
     *                        未实现
     */
    protected void recommend(List<String> blockArticleIds, String[] userId, LanguageType languageType) {
        //TODO
    }

    /**
     * 针对特定用户返回推荐结果
     *
     * @param blockArticleIds 用户屏蔽的文章
     * @param userId          用户id/设备id
     * @param languageType    语言环境
     */
    protected abstract void recommend(List<String> blockArticleIds, String userId, LanguageType languageType);

    /**
     * 向redis中给用户设置推荐文章
     *
     * @param recommendArticleIdsList
     * @param userId
     */
    protected void setRecommendArticles(List<String> recommendArticleIdsList, String userId) {
        StringBuilder stringBuilder = new StringBuilder();
        recommendArticleIdsList.forEach(c -> stringBuilder.append("," + c));
        /**
         * 添加到等待推荐列表
         */
        if (stringBuilder.length() != 0) {
            if (redisUtils.hasKey(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId)) {
                String recommendArticleIds = getRecommendArticleIds(userId);
                redisUtils.set(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId, recommendArticleIds + "," + stringBuilder.substring(1, stringBuilder.length()));
            } else {
                redisUtils.set(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId, stringBuilder.substring(1, stringBuilder.length()));
            }
        }
    }

    /**
     * 获取已经推荐过的文章列表
     *
     * @param userId
     * @return
     */
    protected RecommendedArticles getRecommendedArticles(String userId) {
        RecommendedArticles recommendedArticles = null;
        if (redisUtils.hasKey(RedisKeyPrefix.RECOMMENDED_ARTICLE_IDS + userId)) {
            recommendedArticles = (RecommendedArticles) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.RECOMMENDED_ARTICLE_IDS + userId));
            recommendedArticles.updateRecommendedArticles(propGetKit.getPreviousDays());
        }
        return recommendedArticles;
    }

    /**
     * 获取用户浏览历史
     *
     * @param userId
     * @return
     */
    protected ViewedArticles getViewedArticles(String userId) {
        ViewedArticles viewedArticles = null;
        if (redisUtils.hasKey(RedisKeyPrefix.VIEW_ARTICLE_IDS + userId)) {
            viewedArticles = (ViewedArticles) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.VIEW_ARTICLE_IDS + userId));
            viewedArticles.updateViewedArticles(propGetKit.getPreviousDays());
        }
        return viewedArticles;
    }

    /**
     * 获取正在推荐的文章
     *
     * @param userId
     * @return
     */
    protected String getRecommendArticleIds(String userId) {
        String recommendArticleIds = null;
        if (redisUtils.hasKey(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId)) {
            recommendArticleIds = redisUtils.get(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId);
        }
        return recommendArticleIds;
    }

    /**
     * 获取用户标签
     *
     * @param userId
     * @param languageType
     * @return
     */
    protected UserTags getUserTags(String userId, LanguageType languageType) {
        UserTags userTags = null;
        if (redisUtils.hasKey(RedisKeyPrefix.getUserTag(languageType) + userId)) {
            userTags = (UserTags) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.getUserTag(languageType) + userId));
            userTags.updateUserTag(propGetKit.getPreviousDays());
        }
        return userTags;
    }

    /**
     * 获取标签下的文章
     *
     * @param tagId
     * @param languageType
     * @return
     */
    protected List<String> getTagArticleIds(String tagId, LanguageType languageType) {
        String tagArticles = null;
        if (redisUtils.hasKey(RedisKeyPrefix.getTag(languageType) + tagId)) {
            tagArticles = redisUtils.get(RedisKeyPrefix.getTag(languageType) + tagId);
        }
        return tagArticles == null ? null : Arrays.asList(tagArticles.split(","));
    }

    /**
     * 获取相似用户列表
     *
     * @param userId
     * @return
     */
    protected SimilarityUsers getSimilarityUsers(String userId) {
        SimilarityUsers similarityUsers = null;
        if (redisUtils.hasKey(RedisKeyPrefix.SIMILARITY_USERS + userId)) {
            similarityUsers = (SimilarityUsers) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.SIMILARITY_USERS + userId));
            similarityUsers.updateSimilarityUsers(propGetKit.getPreviousDays());
        }else{
            //TODO
//            asyncService.findSimilarityUsers(userId, propGetKit.getSimilarityUserNum());
        }
        return similarityUsers;
    }

    protected Set<String> getAllBlockArticleIdsSet(String userId, List blockArticleIds) {
        String recommendArticleIds = getRecommendArticleIds(userId);
        ViewedArticles viewedArticles = getViewedArticles(userId);
        RecommendedArticles recommendedArticles = getRecommendedArticles(userId);
        Set<String> newBlockArticleIdsSet = CommonUtils.getSetByLists(blockArticleIds
                , recommendArticleIds == null ? null : Arrays.asList(recommendArticleIds.split(","))
                , viewedArticles == null ? null : viewedArticles.getViewedArticlesList(propGetKit.getCFValidDay())
                , recommendedArticles == null ? null : recommendedArticles.getRecommendedArticlesList(propGetKit.getBeforeDays())
        );
        return newBlockArticleIdsSet;
    }
}

